/* $Id:generator.cc jjs $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

// TODO (jurjen#1#): xslt transform to openoffice format for new set of rules (based on the xml dumps)
// TODO (jurjen#1#): add better map data structure, including more comprimized records
// TODO (jurjen#2#): read routine with the new data format, should be easier than the current defintion
// TODO (jurjen#2#): automatic handling of parent relations
// TODO (jurjen#2#): position in imports of records between child records of other type

#include <iostream>
#include <fstream>
#include "../general/util.h"
#include "../general/general.h"
#include "../general/arguments.h"
#include "record.h"

class generator {
public:
    generator() {
        dir="";
        definitions="~/devel/moros/doc/server_def.txt";
        load_save=false;
        xml_dump=false;
    }
    void xml();
    string dir;           // output directory for source files
    string definitions;
    bool load_save;
    bool xml_dump;
    void def_file(string file);
    void show_record();
    void write_code();
    void read_definition();
};

void generator::xml() {
    ofstream f;
    f.open("/tmp/dump.xml");
    loopRecords(record) {
        record->xml(f);
    }
    f.close();
}

// dump information about the records to the standard output

void generator::show_record() {
    loopRecords(record) {
        cout << "record: " << record->getName();
        if (record->getRelations()) cout << " <relations>";
        if (record->getMain()) cout << " <main>";
        if (record->getOrdered()) cout << " <ordered>";
        if (record->getNamed()) cout << " <named>";
        if (record->getIdField()!="") cout << " {" << record->getIdField() << "}";
        cout <<endl;
        loopRecordFields(record,fld) {
            cout << "  field " << fld->getName() << " type " << fld->showType();
            if (fld->showType()=="children" || fld->showType()=="relation" || fld->showType()=="parent") {
                cout << "(" << fld->getRelated() . getName() << ")";
            }
            cout << endl;
        }
    }
}

#include <sys/types.h>
#include <sys/stat.h>

// return true when a file exists

bool is_file(string f) {
    struct stat st;
    cout << "Stat " << f << endl;
    cout.flush();
    return stat(f.c_str(),&st) > -1;
}

// convert a string to only upper case characters

string upper(string s) {
    string res;
    for (size_t i=0; i<s.length(); i++) {
        res+=toupper(s[i]);
    }
    return res;
}

// get the C++ code of the type of a field

string get_ftype(const field *f) {
    switch(f->getType()) {
    case field::type_number:
    case field::type_enumerate:
    case field::type_position:  return "int ";
    case field::type_float:     return "double ";
    case field::type_string:    return "string ";
    case field::type_parent:
    case field::type_relation:  return "class "+f->getRelated().getName()+" *";
    case field::type_children:
        if (f->getRelated().getNamed()) return "map<string,class "+f->getRelated().getName()+"> ";
        else return "map<int,class "+f->getRelated().getName()+"> ";
    case field::type_boolean:   return "bool ";
    }
    return "";
}

// get the C++ code to initialize a field

string get_finit(const field *f) {
    switch(f->getType()) {
    case field::type_number:
    case field::type_position:  return "0";
    case field::type_enumerate: return "-1";
    case field::type_float:     return "0.0";
    case field::type_string:    return "\"\"";
    case field::type_parent:
    case field::type_relation:  return "0";
    case field::type_children:
        if (f->getRelated().getNamed()) return "map<string,class "+f->getRelated().getName()+">()";
        else return "map<int,class "+f->getRelated().getName()+">()";
    case field::type_boolean:   return "false";
    }
    return "";
}

// get a string with the first character in upper case

string up_name(const string n) {
    string res;
    res+=toupper(n[0]);
    return res+n.substr(1);
}

// convert the first character of a field name to upper case character

string up_name(const field *f) {
    string name=f->getName();
    string res;
    res+=toupper(name[0]);
    return res+name.substr(1);
}

// convert the first character of a record name to upper case character

string up_name(const record *r) {
    string name=r->getName();
    string res;
    res+=toupper(name[0]);
    return res+name.substr(1);
}

// write skeletal .h and .c source files

// TODO (jurjen#2#): Are there more checks possible/needed for code through sockets?
// TODO (jurjen#2#): Code to edit records in gnome (seperate source files!)
// TODO (jurjen#3#): Version that uses compressed data in memory (do a speed test with this result)
void generator::write_code() {
    // loop through all the known records
    loopRecords(record) {
        string file=dir+"/"+record->getName();
        ofstream fp;
        // general start of the header file
        fp.open((file+".h").c_str());
        fp << "/* $Id:" << record->getName() <<".h $ */" << endl;
        fp << "/*" << endl;
        fp << "   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>" << endl; // TODO (jurjen#3#) dynamical copyright string
        fp << "   Part of the Moros Project http://moros.sourceforge.net/" << endl << endl; // TODO (jurjen#3#) dynamical project name
        fp << "   This program is free software; you can redistribute it and/or modify" << endl;
        fp << "   it under the terms of the GNU General Public License version 3." << endl;
        fp << "   This program is distributed in the hope that it will be useful," << endl;
        fp << "   but WITHOUT ANY WARRANTY." << endl <<endl;
        fp << "   See the COPYING file for more details." << endl;
        fp << "*/" << endl << endl;
        fp << "#ifndef __" << upper(record->getName()) << "_H" << endl;
        fp << "#define __" << upper(record->getName()) << "_H" << endl << endl;
        fp << "#include <string>" << endl;
        fp << "#include <map>" << endl;
        fp << "#include <list>" << endl;
        fp << "using namespace std;" << endl << endl;
        // include header files for children and pre-define classes for related records
        {loopRecordFields(record, fld) {
            if (fld->getType() == field::type_children) {
                fp << "#include \"" << fld->getRelated().getName() << ".h\"" << endl;
            }
        }}
        fp << endl;
        // define macro's to loop through the child records without knowledge of the data structure of that child
        {loopRecordFields(record, fld) {
            if (fld->getType() == field::type_children) {
                if (fld->getRelated().getOrdered() && fld->getRelated().getNamed()) {
                    fp << "#define loop" << up_name(record) << up_name(fld) << "(r,v) loopSingle((r)->list" << up_name(fld) << "(),v,class " << fld->getRelated().getName() <<",list)" <<endl;
                } else {
                    if (fld->getRelated().getNamed()) {
                        fp << "#define loop" << up_name(record) << up_name(fld) << "(r,v) loopMap((r)->list" << up_name(fld) << "(),v,class " << fld->getRelated().getName() <<")" <<endl;
                    } else {
                        fp << "#define loop" << up_name(record) << up_name(fld) << "(r,v) loopMapi((r)->list" << up_name(fld) << "(),v,class " << fld->getRelated().getName() <<")" <<endl;
                    }
                }
            }
        }}
        // write the class with the protected internal field
        fp << endl << "class " << record->getName() << " {" << endl;
        {loopRecordFields(record, fld) {
            fp << "    " << get_ftype(fld) << fld->getName() << ";";
            if (fld->getDescription()!="") fp << " // " << fld->getDescription();
            fp << endl;
            if (fld->getType()==field::type_children && fld->getRelated().getOrdered() && fld->getRelated().getIdField()=="")
                fp << "    int max" << up_name(fld) << ";" << endl;
        }}
        // write the public functions on this class
        fp << "public:"<<endl;
        fp << "    " << record->getName() << "();" << endl;
        {loopRecordFields(record, fld) {
            fp << endl << "    // Field : " << fld->getName() << endl;
            switch (fld->getType()) {
            case field::type_number: case field::type_float: case field::type_string: case field::type_boolean:
                fp << "    const " << get_ftype(fld) << "get" << up_name(fld) << "() const;" << endl;
                fp << "    void set" << up_name(fld) << "(const " << get_ftype(fld) << "v);" << endl;
                if (fld->getName()=="nr" && fld->getType()==field::type_number) {
                    fp << "    int operator<(const class " << record->getName() << " &ck) const;" << endl;
                }
                break;
            case field::type_position:
                fp << "    const " << get_ftype(fld) << "get" << up_name(fld) << "() const;" << endl;
                fp << "    void set" << up_name(fld) << "(const " << get_ftype(fld) << "v);" << endl;
                fp << "    const string show" << up_name(fld) << "() const;" << endl; break;
            case field::type_enumerate:
                fp << "    enum " << fld->getName() << "_enum{";
                {bool first=true;
                loopFieldValue(fld, val) {
                    if (first) first=false;
                    else fp << ",";
                    fp << fld->getName() << "_" << val->getName();
                }}
                fp << "};" << endl;
                fp << "    const " << get_ftype(fld) << "get" << up_name(fld) << "() const;" << endl;
                fp << "    const string show" << up_name(fld) << "() const;" << endl;
                fp << "    void set" << up_name(fld) << "(const " << get_ftype(fld) << "v);" << endl;
                fp << "    void set" << up_name(fld) << "(const string v);" << endl; break;
            case field::type_relation: case field::type_parent:
                fp << "    class "<<fld->getRelated().getName() << "& get" << up_name(fld) << "() const;" <<endl;
                fp << "    void set" << up_name(fld) << "(class " << fld->getRelated().getName() << " &v);" <<endl; break;
            case field::type_children:
                if (fld->getRelated().getNamed()) {
                    fp << "    class "<<fld->getRelated().getName() << "& get" << up_name(fld) << "(const string v);" <<endl;
                    if (fld->getRelated().getOrdered()) {
                        fp << "    list<class "<< fld->getRelated().getName() << ">& list" << up_name(fld) << "();" <<endl;
                    } else {
                        fp << "    map<string,class "<< fld->getRelated().getName() << ">& list" << up_name(fld) << "();" <<endl;
                    }
                    fp << "    void set" << up_name(fld) << "(class " << fld->getRelated().getName() << " &v);" <<endl;
                    fp << "    void del" << up_name(fld) << "(const string v);" <<endl;
                    fp << "    const bool is" << up_name(fld) << "(const string v) const;" <<endl;
                } else {
                    fp << "    class "<<fld->getRelated().getName() << "& get" << up_name(fld) << "(const int v);" <<endl;
                    fp << "    map<int,class "<< fld->getRelated().getName() << ">& list" << up_name(fld) << "();" <<endl;
                    fp << "    void set" << up_name(fld) << "(class " << fld->getRelated().getName() << " &v);" <<endl;
                    fp << "    void del" << up_name(fld) << "(const int v);" <<endl;
                    fp << "    const bool is" << up_name(fld) << "(const int v) const;" <<endl;
                } break;
            }
        }}
        if (record->getIdField()!="" && record->getOrdered()) {
            fp << "    int operator<(const class " << record->getName() << " &ck) const;" << endl;
        }
        if (load_save || xml_dump) fp << endl;
        if (load_save) {
            fp << "    void save(class channel &ch);" << endl;
            fp << "    void load(class channel &ch);" << endl;
        }
        if (xml_dump) {
            fp << "    void xml(ofstream &f, int l=0, int r=0);" << endl;
        }
        fp << "};"<<endl << endl;
        // when this is a main record write a seperate repository class to access it
        if (record->getMain()) {
            fp << "// Repository" << endl << endl;
            if (record->getOrdered() && record->getNamed()) {
                fp << "#define loop" << up_name(record) << "s(v) loopSingle(" << record->getName() << "_repo.list" << up_name(record) << "(),v,class " << record->getName() <<",list)" <<endl;
            } else if (record->getNamed()) {
                fp << "#define loop" << up_name(record) << "s(v) loopMap(" << record->getName() << "_repo.list" << up_name(record) << "(),v,class " << record->getName() <<")" <<endl;
            } else {
                fp << "#define loop" << up_name(record) << "s(v) loopMapi(" << record->getName() << "_repo.list" << up_name(record) << "(),v,class " << record->getName() <<")" <<endl;
            }
            fp << endl << "class " << record->getName() << "Repository {" << endl;
            if (record->getNamed()) {
                fp << "    map<string," << record->getName() << "> " << record->getName() << "s;" << endl;
                if (record->getOrdered()) {
                    fp << "    int max_nr;" << endl;
                    fp << "public:"<< endl;
                    fp << "    " << record->getName() << "Repository();" << endl;
                } else {
                    fp << "public:"<< endl;
                }
                fp << "    class " << record->getName() << "& get" << up_name(record) << "(const string v);" << endl;
                fp << "    void set" << up_name(record) << "(class " << record->getName() << "& v);" << endl;
                if (record->getOrdered()) {
                    fp << "    list<class " << record->getName() << ">& list" << up_name(record) << "();" << endl;
                } else {
                    fp << "    map<string,class " << record->getName() << ">& list" << up_name(record) << "();" << endl;
                }
                fp << "    void del" << up_name(record) << "(const string v);" << endl;
                fp << "    const bool is" << up_name(record) << "(const string v) const;" << endl;
                fp << "    multimap<string, sigc::signal<void, class " << record->getName() << "& > > relations;" << endl;
            } else {
                fp << "    map<int,class " << record->getName() << "> " << record->getName() << "s;" << endl;
                fp << "    int max_nr;" << endl;
                fp << "public:"<< endl;
                fp << "    " << record->getName() << "Repository();" << endl;
                fp << "    class " << record->getName() << "& get" << up_name(record) << "(const int v);" << endl;
                fp << "    void set" << up_name(record) << "(class " << record->getName() << "& v);" << endl;
                fp << "    map<int,class " << record->getName() << ">& list" << up_name(record) << "();" << endl;
                fp << "    void del" << up_name(record) << "(const int v);" << endl;
                fp << "    const bool is" << up_name(record) << "(const int v) const;" << endl;
            }
            if (load_save) {
                fp << "    bool load(class channel &ch);" << endl;
                fp << "    void save(class channel &ch);" << endl;
            }
            fp << "};" << endl << endl;
            // declare repository variable
            fp << "extern " << record->getName() << "Repository " << record->getName() << "_repo;" << endl << endl;
        }
        fp << "#endif // __" << upper(record->getName()) << "_H" << endl;
        fp.close();
        // write the general part of the implementation file
        fp.open((file+".cc").c_str());
        fp << "/* $Id:" << record->getName() <<".cc $ */" << endl;
        fp << "/*" << endl;
        fp << "   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>" << endl; // TODO (jurjen#3#): dynamical copyright string        fp << "   Part of the Moros Project http://moros.sourceforge.net/" << endl << endl; // TODO (jurjen#3#): dynamical project name
        fp << "   This program is free software; you can redistribute it and/or modify" << endl;
        fp << "   it under the terms of the GNU General Public License version 3." << endl;
        fp << "   This program is distributed in the hope that it will be useful," << endl;
        fp << "   but WITHOUT ANY WARRANTY." << endl <<endl;
        fp << "   See the COPYING file for more details." << endl;
        fp << "*/" << endl << endl;
        fp << "#define DEBUG_ROUTINE \"" << record->getName() << "\"" << endl << endl;
        fp << "#include <sstream>" << endl;
        // include the own header file and header files of related fields
        if (xml_dump) {
            fp << "#include <fstream>" << endl;
            fp << "#include \"../general/util.h\"" << endl;
        }
        fp << "#include \"../general/log.h\"" << endl;
        if (load_save) {
            fp << "#include \"../general/channel.h\"" << endl;
        }
        fp << "#include \""<< record->getName() <<".h\"" << endl;
        {loopRecordFields(record, fld) {
            if (fld->getType() == field::type_relation || fld->getType() == field::type_parent) {
                fp << "#include \"" << fld->getRelated().getName() << ".h\"" << endl;
            }
            if (fld->getType() == field::type_children) {
                if (fld->getRelated().getIdField() != "") {
                    fp << "#include \"" << fld->getRelated().getFields(fld->getRelated().getIdField()).getRelated().getName() << ".h\"" << endl;
                }
            }
        }}
        fp << "#include \"../general/general.h\"" << endl;
        // write a general constructor of this record
        fp << endl << record->getName() << "::" << record->getName() << "() {" << endl;
        {loopRecordFields(record, fld) {
            fp << "    " << fld->getName() << "=" << get_finit(fld) << ";" << endl;
            if (fld->getType()==field::type_children && fld->getRelated().getOrdered() && fld->getRelated().getIdField()=="") fp << "    max" << up_name(fld) << "=0;" << endl;
        }}
        fp << "}" << endl;
        // loop through the fields and write the needed getters and setters to access the fields
        {loopRecordFields(record, fld) {
            fp << endl;
            fp << "// Field : " << fld->getName() << endl;
            fp << endl;
            switch (fld->getType()) {
            case field::type_number: case field::type_float: case field::type_string: case field::type_boolean:
                fp << "const " << get_ftype((fld)) << record->getName() << "::get" << up_name((fld)) << "() const { return " << fld->getName() << "; }" << endl;
                fp << "void " << record->getName() << "::set" << up_name((fld)) << "(const " << get_ftype((fld)) << "v) { " << fld->getName() << "=v; }" << endl;
                if (fld->getName()=="nr" && fld->getType()==field::type_number) {
                    fp << "int " << record->getName() << "::operator<(const class " << record->getName() << " &ck) const { return (this->nr < ck.nr)?1:0; }" << endl;
                }
                break;
            case field::type_position:
                fp << "const " << get_ftype((fld)) << record->getName() << "::get" << up_name((fld)) << "() const { return " << fld->getName() << "; }" << endl;
                fp << "void " << record->getName() << "::set" << up_name((fld)) << "(const " << get_ftype((fld)) << " v) { " << fld->getName() << "=v; }" << endl;
                fp << "const string " << record->getName() << "::show" << up_name((fld)) << "() const {" << endl;
                fp << "    stringstream res;" << endl;
                fp << "    res << \"(\" << (" << fld->getName() << " & 0xffff) << \",\" << (" << fld->getName() << " << 16) << \")\";" << endl;
                fp << "    return res.str();" << endl;
                fp << "}" << endl; break;
            case field::type_enumerate:
                fp << "const string " << record->getName() << up_name((fld)) << "[]={";
                {bool first=true;
                loopFieldValue((fld), val) {
                    if (first) first=false;
                    else fp << ",";
                    fp << "\"" << val->getName() << "\"";
                }}
                fp << "};" << endl;
                fp << "const " << get_ftype((fld)) << record->getName() << "::get" << up_name((fld)) << "() const { return " << fld->getName() << "; }" << endl;
                fp << "const string " << record->getName() << "::show" << up_name((fld)) << "() const {" << endl;
                fp << "    if (" << fld->getName()<<"<0) return \"\";" << endl;
                fp << "    else return " << record->getName() << up_name((fld)) << "[" << fld->getName() <<"];"<< endl;
                fp << "}" << endl;
                fp << "void " << record->getName() << "::set" << up_name((fld)) << "(const " << get_ftype((fld)) << " v) {" << endl;
                fp << "    if (v<0 || (unsigned int)v >= sizeof " << record->getName() << up_name((fld)) << "/sizeof " << record->getName() << up_name((fld)) << "[0]) " << fld->getName() << "=-1;" << endl;
                fp << "    else " << fld->getName() << "=v;" << endl;
                fp << "}" << endl;
                fp << "void " << record->getName() << "::set" << up_name((fld)) << "(const string v) {" << endl;
                fp << "    " << fld->getName() << "=-1;" << endl;
                fp << "    loopArray(" << record->getName() << up_name((fld)) << ", tp, string) {" << endl;
                fp << "        if (v==*tp) {" << fld->getName() << "=tp_i; break; }" << endl;
                fp << "    }"<<endl;
                if (record->getNamed()) {
                    if (record->getIdField()!="")
                        fp << "    if (" << fld->getName() << "==-1) dbg_state(\"Enumerate " << record->getName() << "." << fld->getName() << " %s has unknown '%s'\", get" << up_name(record->getIdField()) << "().getName().c_str(), v.c_str());" << endl;
                    else
                        fp << "    if (" << fld->getName() << "==-1) dbg_state(\"Enumerate " << record->getName() << "." << fld->getName() << " %s has unknown '%s'\", getName().c_str(), v.c_str());" << endl;
                } else
                    fp << "    if (" << fld->getName() << "==-1) dbg_state(\"Enumerate " << record->getName() << "." << fld->getName() << " unknown '%s'\", v.c_str());" << endl;
                fp << "}" << endl; break;
            case field::type_relation: case field::type_parent:
                fp << "class " << fld->getRelated().getName() << "& " << record->getName() << "::get" << up_name((fld)) << "() const { if (" << fld->getName() << "==0) throw nullpointer(); return *" << fld->getName() <<"; }" <<endl;
                fp << "void " << record->getName() << "::set" << up_name((fld)) << "(class " << fld->getRelated().getName() << " &v) { "<< fld->getName() << "=&v; }" <<endl; break;
            case field::type_children: // inherit name/nr from related record when getId_field is filled
                if (fld->getRelated().getNamed()) {
                    fp << "class " << fld->getRelated().getName() << "& " << record->getName() << "::get" << up_name((fld)) << "(const string v) { return " << fld->getName() << "[v]; }" <<endl;
                    if (fld->getRelated().getOrdered()) {
                        fp << "list<class "<< fld->getRelated().getName() << ">& "<< record->getName() << "::list" << up_name((fld)) << "() {" <<endl;
                        fp << "    static list<class "<< fld->getRelated().getName() << "> res;" << endl;
                        fp << "    res.clear();" << endl;
                        fp << "    loopMap(" << fld->getName() << ",v,class "<< fld->getRelated().getName() <<") {" << endl;
                        fp << "        res.push_back(*v);" << endl;
                        fp << "    }" << endl;
                        fp << "    res.sort();" << endl;
                        fp << "    return res;" << endl;
                        fp << "}" << endl;
                    } else {
                        fp << "map<string,class "<< fld->getRelated().getName() << ">& " << record->getName() << "::list" << up_name((fld)) << "() { return " << fld->getName() << "; }" <<endl;
                    }
                    fp << "void " << record->getName() << "::set" << up_name((fld)) << "(class " << fld->getRelated().getName() << " &v) {" <<endl;
                    if (fld->getRelated().getIdField()!="") {
                        fp << "    " << fld->getName() << "[v.get"<< up_name(fld->getRelated().getIdField()) << "().getName()]=v;" << endl;
                    } else {
                        if (fld->getRelated().getOrdered()) {
                            fp << "    if (v.getNr()==0) v.setNr(++max" << up_name((fld)) <<");" << endl;
                        }
                        fp << "    if (v.getName()==\"\") throw unnamed();" << endl;
                        fp << "    " << fld->getName() << "[v.getName()]=v;" << endl;
                    }
                    fp << "}" << endl;
                    fp << "void " << record->getName() << "::del" << up_name((fld)) << "(const string v) { " << fld->getName()<<".erase(v); }" <<endl;
                    fp << "const bool " << record->getName() << "::is" << up_name((fld)) << "(const string v) const { return " << fld->getName() << ".count(v)>0; }" <<endl; break;
                } else {
                    fp << "class " << fld->getRelated().getName() << "& " << record->getName() << "::get" << up_name((fld)) << "(const int v) { return " << fld->getName() << "[v]; }" <<endl;
                    fp << "map<int,class "<< fld->getRelated().getName() << ">& " << record->getName() << "::list" << up_name((fld)) << "() { return " << fld->getName() << "; }" <<endl;
                    fp << "void " << record->getName() << "::set" << up_name((fld)) << "(class " << fld->getRelated().getName() << " &v) {" <<endl;
                    if (fld->getRelated().getIdField()!="") {
                        fp << "    " << fld->getName() << "[v.get"<< up_name(fld->getRelated().getIdField()) << "().getNr()]=v;" << endl;
                    } else {
                        fp << "    if (v.getNr()==0) v.setNr(++max" << up_name((fld)) <<");" << endl;
                    }
                    fp << "    " << fld->getName() << "[v.getNr()]=v;" << endl;
                    fp << "}" << endl;
                    fp << "void " << record->getName() << "::del" << up_name((fld)) << "(const int v) { " << fld->getName()<<".erase(v); }" <<endl;
                    fp << "const bool " << record->getName() << "::is" << up_name((fld)) << "(const int v) const { return " << fld->getName() << ".count(v)>0; }" <<endl; break;
                }
            }
        }}
        if (record->getIdField()!="" && record->getOrdered()) {
            fp << "int " << record->getName() << "::operator<(const class " << record->getName() << " &ck) const {" << endl;
            fp << "    return (get" << up_name(record->getIdField()) << "().getNr() < ck.get" << up_name(record->getIdField()) << "().getNr())?1:0;" << endl;
            fp << "}" << endl;
        }
        if (load_save) {
            fp << endl << "void " << record->getName() << "::save(class channel &ch) {" << endl;
            fp << "    ch.write(\"" << record->getName() << "\", \"\");" << endl;
            {loopRecordFields(record, fld) {
                switch (fld->getType()) {
                case field::type_number: case field::type_float: case field::type_string: case field::type_boolean: case field::type_position: case field::type_enumerate:
                    fp << "    ch.write(\"" << fld->getAbbreviation() << "\", get" << up_name(fld) << "());" << endl; break;
                case field::type_relation: case field::type_parent:
                    if (fld->getRelated().getIdField()!="") {
                        if (fld->getRelated().getNamed()) {
                            fp << "    ch.write(\"" << fld->getAbbreviation() << "\", get" << up_name(fld) << "().get" << up_name(fld->getRelated().getIdField()) << "().getName());" << endl; break;
                        } else {
                            fp << "    ch.write(\"" << fld->getAbbreviation() << "\", get" << up_name(fld) << "().get" << up_name(fld->getRelated().getIdField()) << "().getNr());" << endl; break;
                        }
                    } else {
                        if (fld->getRelated().getNamed()) {
                            fp << "    ch.write(\"" << fld->getAbbreviation() << "\", get" << up_name(fld) << "().getName());" << endl; break;
                        } else {
                            fp << "    ch.write(\"" << fld->getAbbreviation() << "\", get" << up_name(fld) << "().getNr());" << endl; break;
                        }
                    }
                case field::type_children:
                    fp << "    loop" << up_name(record) << up_name(fld) << "(this, s_" << fld->getName() << ") {" << endl;
                    fp << "        s_" << fld->getName() << "->save(ch);" << endl;
                    fp << "    }"<< endl; break;
                }
            }}
            fp << "}" << endl << endl;
            fp << "void " << record->getName() << "::load(class channel &ch) {" << endl;
            {loopRecordFields(record, fld) {
                switch (fld->getType()) {
                case field::type_number: case field::type_boolean: case field::type_position:
                    fp << "    if (ch.key==\"" << fld->getAbbreviation() << "\" || ch.key==\"" << fld->getName() << "\") { set" << up_name(fld) << "(atoi(ch.value.c_str())); ch.next(); }" << endl; break;
                case field::type_float:
                    fp << "    if (ch.key==\"" << fld->getAbbreviation() << "\" || ch.key==\"" << fld->getName() << "\") { set" << up_name(fld) << "(atof(ch.value.c_str())); ch.next(); }" << endl; break;
                case field::type_string: case field::type_enumerate:
                    fp << "    if (ch.key==\"" << fld->getAbbreviation() << "\" || ch.key==\"" << fld->getName() << "\") { set" << up_name(fld) << "(ch.value); ch.next(); }" << endl; break;
                case field::type_relation: case field::type_parent:
                    fp << "    if (ch.key==\"" << fld->getAbbreviation() << "\" || ch.key==\"" << fld->getName() << "\") {" << endl;
                    if (fld->getRelated().getNamed()) {
                        fp << "        if (!" << fld->getRelated().getName() << "_repo.is" << up_name(fld->getRelated().getName()) << "(ch.value)) {" << endl;
                        fp << "            dbg_state(\"link unknown " << fld->getRelated().getName() << " %s\", ch.value.c_str());" << endl;
                        fp << "            class " << fld->getRelated().getName() << " rec;" << endl;
                        fp << "            rec.setName(ch.value);" << endl;
                        fp << "            " << fld->getRelated().getName() << "_repo.set" << up_name(fld->getRelated().getName()) << "(rec);" << endl;
                        if (fld->getRelated().getOrdered()) fp << "            rec.setNr(0);" << endl;
                        fp << "        }" << endl;
                        fp << "        set" << up_name(fld) << "(" << fld->getRelated().getName() << "_repo.get" << up_name(fld->getRelated().getName()) << "(ch.value));" << endl;
                        fp << "        ch.next();" << endl;
                    }
                    fp << "    }" << endl; break;
                case field::type_children:
                    fp << "    while (ch.key==\"" << fld->getRelated().getName() << "\" || ch.key==\"" << fld->getName() << "\") {" << endl;
                    fp << "        ch.next();" << endl;
                    fp << "        class " << fld->getRelated().getName() << " rec;" << endl;
                    fp << "        rec.load(ch);" << endl;
                    fp << "        set" << up_name(fld) << "(rec);" << endl;
                    fp << "    }" << endl; break;
                }
            }}
            fp << "}" << endl;
        }
        if (xml_dump) {
            fp << endl;
            fp << "void " << record->getName() << "::xml(ofstream &f, int l, int r) {" << endl;
            fp << "    f << ncpy(l, \"  \") << \"<" << record->getName();
            {loopRecordFields(record, fld) {
                switch (fld->getType()) {
                case field::type_number: case field::type_float:
                    fp << " " << fld->getName() << "=\\\"\" << get" << up_name(fld) << "() <<" << endl << "    \"\\\""; break;
                case field::type_string:
                    fp << " " << fld->getName() << "=\\\"\" << xml_encode(get" << up_name(fld) << "()) <<" << endl << "    \"\\\""; break;
                case field::type_boolean:
                    fp << " " << fld->getName() << "=\\\"\" << (get" << up_name(fld) << "()?\"true\":\"false\") <<" << endl << "    \"\\\""; break;
                case field::type_position:
                    fp << " " << fld->getName() << "_x=\\\"\" << (get" << up_name(fld) << "() & 0xffff) <<" << endl << "    \"\\\"";
                    fp << " " << fld->getName() << "_y=\\\"\" << (get" << up_name(fld) << "() << 16) <<" << endl << "    \"\\\""; break;
                case field::type_enumerate:
                    fp << " " << fld->getName() << "=\\\"\" << show" << up_name(fld) << "() <<" << endl << "    \"\\\""; break;
                }
            }}
            bool sub=false;
            bool first=true;
            {loopRecordFields(record, fld) {
                switch (fld->getType()) {
                case field::type_relation: case field::type_parent:
                    if (!sub) fp << ">\" << endl;" << endl;
                    sub=true;
                    if (first) {
                        first=false;
                        fp << "    if (r<2) try {" << endl;
                    }
                    fp << "        get" << up_name(fld) << "().xml(f,l+1,r+1);" << endl;
                }
            }}
            if (!first) fp << "    } catch (nullpointer n) {}" << endl;
            first=true;
            {loopRecordFields(record, fld) {
                switch (fld->getType()) {
                case field::type_children:
                    if (!sub) fp << ">\" << endl;" << endl;
                    sub=true;
                    if (first) {
                        first=false;
                        fp << "    if (r==0) {" << endl;
                    }
                    fp << "        loop" << up_name(record) << up_name(fld) << "(this, s_" << fld->getName() << ") {" << endl;
                    fp << "            s_" << fld->getName() << "->xml(f,l+1);" << endl;
                    fp << "        }"<< endl; break;
                }
            }}
            if (!first) fp << "    }" << endl;
            if (sub) {
                fp << "    f << ncpy(l, \"  \") << \"</" << record->getName() << ">\" << endl;" << endl;
            } else {
                fp << "/>\" << endl;" << endl;
            }
            fp << "}" << endl;
        }
        // when needed write the repository functions to access this main record directly
        if (record->getMain()) {
            fp << endl;
            fp << "// Repository" << endl;
            // define repository variable
            fp << endl << record->getName() << "Repository " << record->getName() << "_repo;" << endl << endl;
            if (record->getNamed()) {
                if (record->getOrdered()) {
                    fp << record->getName() << "Repository::" << record->getName() << "Repository() { max_nr=0; }" << endl;
                }
                fp << "class " << record->getName() << "& " << record->getName() << "Repository::get" << up_name(record) << "(const string v) { return " << record->getName() << "s[v]; }" << endl;
                fp << "void " << record->getName() << "Repository::set" << up_name(record) << "(class " << record->getName() << "& v) {" << endl;
                if (record->getIdField()!="") {
                    fp << "    " << record->getName() << "s[v.get"<< up_name(record->getIdField()) << "().getName()]=v;" << endl;
                } else {
                    if (record->getOrdered()) {
                        fp << "    if (v.getNr()==0) v.setNr(++max_nr);" << endl;
                    }
                    fp << "    if (v.getName()==\"\") throw unnamed();" << endl;
                    fp << "    "<< record->getName() << "s[v.getName()]=v;" << endl;
                }
                fp << "}" << endl;
                if (record->getOrdered()) {
                    fp << "list<class "<< record->getName() << ">& "<< record->getName() << "Repository::list" << up_name(record) << "() {" <<endl;
                    fp << "    static list<class "<< record->getName() << "> res;" << endl;
                    fp << "    res.clear();" << endl;
                    fp << "    loopMap(" << record->getName() << "s,v,class "<< record->getName() <<") {" << endl;
                    fp << "        res.push_back(*v);" << endl;
                    fp << "    }" << endl;
                    fp << "    res.sort();" << endl;
                    fp << "    return res;" << endl;
                    fp << "}" << endl;
                } else {
                    fp << "map<string,class " << record->getName() << ">& " << record->getName() << "Repository::list" << up_name(record) << "() { return " << record->getName() << "s; }" << endl;
                }
                fp << "void " << record->getName() << "Repository::del" << up_name(record) << "(const string v) { " << record->getName() << "s.erase(v); }" << endl;
                fp << "const bool " << record->getName() << "Repository::is" << up_name(record) << "(const string v) const { return " << record->getName() << "s.count(v)>0; }" << endl;
            } else {
                fp << record->getName() << "Repository::" << record->getName() << "Repository() { max_nr=0; }" << endl;
                fp << "class " << record->getName() << "& " << record->getName() << "Repository::get" << up_name(record) << "(const int v) { return " << record->getName() << "s[v]; }" << endl;
                fp << "void " << record->getName() << "Repository::set" << up_name(record) << "(class " << record->getName() << "& v) {" << endl;
                if (record->getIdField()!="") {
                    fp << "    " << record->getName() << "s[v.get"<< up_name(record->getIdField()) << "().getNr()]=v;" << endl;
                } else {
                    fp << "    if (v.getNr()==0) v.setNr(++max_nr);" << endl;
                    fp << "    "<< record->getName() << "s[v.getNr()]=v;" << endl;
                }
                fp << "}" << endl;
                fp << "map<int,class " << record->getName() << ">& " << record->getName() << "Repository::list" << up_name(record) << "() { return " << record->getName() << "s; }" << endl;
                fp << "void " << record->getName() << "Repository::del" << up_name(record) << "(const int v) { " << record->getName() << "s.erase(v); }" << endl;
                fp << "const bool " << record->getName() << "Repository::is" << up_name(record) << "(const int v) const { return " << record->getName() << "s.count(v)>0; }" << endl;
            }
            if (load_save) {
                fp << "void " << record->getName() << "Repository::save(class channel &ch) {" << endl;
                fp << "    loop" << up_name(record) << "s(rec) {" << endl;
                fp << "        rec->save(ch);" << endl;
                fp << "    }"<< endl;
                fp << "}" << endl << endl;
                fp << "bool " << record->getName() << "Repository::load(class channel &ch) {" << endl;
                fp << "    if (ch.key==\"" << record->getName() << "\") {" << endl;
                fp << "        ch.next();" << endl;
                fp << "        class " << record->getName() << " rec;" << endl;
                fp << "        rec.load(ch);" << endl;
                fp << "        set" << up_name(record) << "(rec);" << endl;
                fp << "        return true;" << endl;
                fp << "    }" << endl;
                fp << "    return false;" << endl;
                fp << "}" << endl;
            }
        }
        fp.close();
    }
}

// ***********************************************
//
// Start of parsing routines of the defintion file
//
// ***********************************************

record crecord;     // data of current record
field cfield;       // data of current field
string line="";     // not parsed part of the line from the source file
string recname="";  // name of current record
string fldname="";  // name of current field
string type="";     // type of current field
string parm="";     // parameter to type

// structure to remember the relations, they can only be solved after reading all the records

struct relation {
    string rec; // name of the record
    string fld; // name of the field
    string rel; // name of the related record that can be defined later
    int lnr; // current line
};

// return the part of `line` till the first space or the whole `line`, cut this part from `line`

string word() {
    size_t pos=line.find(" ");
    string res=line.substr(0,pos);
    line=line.substr(pos+1);
    return res;
}

// return the part of `line` till the bracket or the whole `line` and remember the part between the brackets in `parm`

string get_type(string *parm) {
    size_t pos=line.find("(");
    if (pos==string::npos) return line;
    string res=line.substr(0,pos);
    *parm=line.substr(pos+1,line.length()-pos-2);
    return res;
}

bool match(const string m) {
    if (line.substr(0, m.length()) == m) {
        line=line.substr(m.length());
        return true;
    }
    return false;
}

// parse the defintion file and fill the internal data structure with the results


void generator::read_definition() {
    int linenr=0;
    list<relation> solves;
    const string file=filename(definitions);
    ifstream fp (file.c_str());
    if (!fp.is_open()) {
        cerr << "Cannot read file: '" << definitions << "'" << endl;
        exit(1);
    }
    while (!fp.eof()) {
        linenr++;
        getline(fp, line);
        if (match("directory ")) {
            dir=filename(line);
            cout << "Directory: '" << dir << "'" << endl;
        } else if (match("routine ")) {
            if (match("load_save")) load_save=true;
            if (match("xml_dump")) xml_dump=true;
        } else if (match("record ")) {
            if (recname!="") {
                record_repo.setRecord(crecord);
            }
            recname=line;
            fldname="";
            crecord=record_repo.getRecord(recname);
            crecord.setName(recname);
        } else if (match("field ")) {
            fldname=word();
            cfield=field();
            cfield.setName(fldname);
            parm="";
            type=get_type(&parm);
            cfield.setType(type);
            if (cfield.getType()<0) {
                cerr << "Unknown type found for field '" << recname << "." << fldname << "' at line " << linenr << endl;
                exit(1);
            }
            if (parm == "") {
                if (type == "relation" || type == "parent" || type == "children") {
                    cerr << "Relation type for field '" << recname << "." << fldname << "' without related record at line " << linenr << endl;
                }
            } else if (type == "enumerate") {
                string enum_part=split(&parm, ",");
                while(enum_part != "") {
                    value vl;
                    if (enum_part[0]==' ') vl.setName(enum_part.substr(1));
                    else vl.setName(enum_part);
                    cfield.setValue(vl);
                    enum_part=split(&parm, ",");
                }
            } else {
                relation r={recname,fldname,parm,linenr};
                solves.push_back(r);
            }
            if (fldname=="name" && type=="string") crecord.setNamed(true);
            if (fldname=="nr" && type=="number") crecord.setOrdered(true);
            crecord.setFields(cfield);
        } else if (match("# ")) {
            if (fldname!="") {
                cfield.setDescription(line);
                crecord.setFields(cfield);
            }
        }
    }
    if (recname != "") record_repo.setRecord(crecord);
    fp.close();
    // loop through the found relations in the different fields and find information about the corresponding record
    loopSingle(solves,rl,relation,list) {
        if (record_repo.isRecord(rl->rel)) {
            crecord=record_repo.getRecord(rl->rec);
            cfield=crecord.getFields(rl->fld);
            record& crel=record_repo.getRecord(rl->rel);
            cfield.setRelated(crel);
            crecord.setFields(cfield);
            if (cfield.getType()!=field::type_children) crel.setRelations(true);
            if (cfield.getType()==field::type_children) crel.setMain(false);
            record_repo.setRecord(crecord);
        } else {
            cerr << "Error on line " << rl->lnr << " related record '" << rl->rel << "' not defined" << endl;
            exit(1);
        }
    }
    // finish the different records, when a record doesn't have a name or nr get that from the first related record
    {loopRecords(record) {
        if (record->getRelations() && !record->getMain()) record->setMain(true);
        if (!record->getNamed() && !record->getOrdered()) {
            loopRecordFields(record, fld) {
                if (fld->getType()==field::type_relation) {
                    record->setIdField(fld->getName());
                    record->setNamed(fld->getRelated().getNamed());
                    record->setOrdered(fld->getRelated().getOrdered());
                    break;
                }
            }
        }
        if (!record->getNamed() && !record->getOrdered()) {
            cerr << "No indentifier for '" << record->getName() << "' possible" << endl;
            exit(1);
        }
    }}
}

void generator::def_file(string file) {
   definitions=file;
}

#define arg_sig(t,a,s) {sigc::signal<void, std::string> s##_signal; \
    s##_signal.connect(sigc::mem_fun(gen, &generator::s)); \
    arg_add(t, "", "", a, s##_signal);}

int main(int argc, char* argv[]) {
    generator gen=generator();
    sigc::signal<void, std::string> dummy;
    arg_add(ARG_PROGRAM, "", "", "moros_generator", dummy);
    arg_add(ARG_VERSION, "", "", "0.1", dummy);
    arg_add(ARG_DATE, "", "", "December 06 2007", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "Copyright (C) 2007, Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "This is free software.  You may redistribute copies of it under the terms of", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "the GNU General Public License version 3 <http://www.gnu.org/licenses/gpl.html>.", dummy);
    arg_add(ARG_COPYRIGHT, "", "", "There is NO WARRANTY, to the extent permitted by law.", dummy);
    arg_add(ARG_USAGE, "", "", "[OPTION]...", dummy);
    arg_add(ARG_DESCR, "", "*", "Generate skeletal classes for data structures.", dummy);
    arg_sig("D", "Definition file, default: ~/devel/moros/doc/server_def.txt", def_file);
    arg_extra("definitions", "FILE");
    arg_add(ARG_BUGS, "", "", "info@moroshq.org", dummy);
    arg_parse(argc, argv);

    gen.read_definition();
    gen.xml();
    gen.show_record();
    gen.write_code();
}
